// Copyright (c) 2008 Matthew Johnson
// Licensed under the MIT License (see license.txt)
package actionml.interaction {
	import actionml.Global;
	
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.ui.Keyboard;
	
	public class BasicInteractions {
		public static function initialize() {
			Global.initializeExtension(BasicInteractions, DisplayObject);
		}
		public static var instanceMethods:Object = {
			clickOnEnter: function() {
				if (this.isAlready("ClickingOnEnter")) return;
				var o:DisplayObject = this;
				var hit:DisplayObject = this.hit || this;
				this.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent) {
					if (e.keyCode == Keyboard.ENTER) {
						o.handlingAccessibilityClick = true;					
						hit.handlingAccessibilityClick = true;
						Global["handlingAccessibilityClick"] = true;
						hit.dispatchEvent(new MouseEvent(MouseEvent.CLICK, true, true, hit.mouseX, hit.mouseY, null));
						Global["handlingAccessibilityClick"] = false;
						delete hit.handlingAccessibilityClick;
						delete o.handlingAccessibilityClick;
					}
				});
			},
			updateFocusIndicator: function(e:MouseEvent = null) {
				var p:Point;
				if (e) {
					p = this.globalToLocal(new Point(e.stageX, e.stageY));
				} else {
					p = this.globalToLocal(new Point(Global.stage.mouseX, Global.stage.mouseY));					
				}
				this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT, true, true, p.x, p.y));
			},
			forceMouseOut: function() {
				this.visible = false; // fake out the mouse out handlers so they don't think o is under the mouse
				this.dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT, true, true, this.stage.mouseX, this.stage.mouseY));
				this.visible = true;
			},
			blockMouseFocus: function() {
				// does not use hit since it wants to capture the event on the way up
				this.addEventListener(MouseEvent.MOUSE_OVER, function(e:MouseEvent) {
					e.stopImmediatePropagation();
				}, false, 999999);
				this.addEventListener(MouseEvent.MOUSE_OUT, function(e:MouseEvent) {
					e.stopImmediatePropagation();
				}, false, 999999);	
			},
			displayIndicatorWhenFocused: function(focusIndicatorName:String = "focusIndicator") {
				if (this.isAlready("DisplayingIndicatorWhenFocused")) return;
				if (!focusIndicatorName) {
					focusIndicatorName = "focusIndicator";
				}
				var o:DisplayObject = this;
				
				if (Global.stage.focus == o) {
					o.showChild(focusIndicatorName);
				} else {
					o.hideChild(focusIndicatorName);
				}
				this.triggerFocusInActions();
				this.triggerFocusOutActions();
				this.addEventListener("focus", function() {
					o.showChild(focusIndicatorName);
				});
				this.addEventListener("blur", function() {
					o.hideChild(focusIndicatorName);
				});
			},
			triggerFocusInActions: function() {
				if (this.isAlready("TriggeringFocusInActions")) return;
				var hit:DisplayObject = this.hit || this;
				hit.addEventListener(FocusEvent.FOCUS_IN, overHandler);
				hit.addEventListener(MouseEvent.MOUSE_OVER, overHandler);
				var o:DisplayObject = this;
				function overHandler(e:Event) {
					if (o.blockFocusIndicator) {
						return;
					}
					if (e is MouseEvent && Global["hover"] == o) {
						return;
					}
					if (e is MouseEvent && o.hit && !o.hit["isThisOrChildTopObjectUnderPoint"](MouseEvent(e).stageX, MouseEvent(e).stageY, true)) {
						return;
					}
					if (e is MouseEvent) {
						if (Global["hover"] is DisplayObject && Global["hover"] != o) {
							Global["hover"].dispatchEvent(new MouseEvent(MouseEvent.MOUSE_OUT, true, true, o.stage.mouseX, o.stage.mouseY, null));
						}
						Global["hover"] = o;
					}
					o.doEvent("focus");
				}
			},
			triggerFocusOutActions: function() {
				if (this.isAlready("TriggeringFocusOutActions")) return;
				var t:Object = BasicInteractions["getTracer"]({method:"triggerFocusOutActions", context: "xml"});
				if ("hoverExtenders" in this && !(this.hoverExtenders is Array)) {
					var hoverExtenders:Array = []
					for each (var s:String in this.hoverExtenders.split(/\s*,\s*/)) {
						var hoverExtender:DisplayObject = this.getValueForKeypath(s);
						if (hoverExtender && hoverExtender is DisplayObject) {
							hoverExtenders.push(hoverExtender);
						} else {
							t.error(s + " is included in the hoverExtenders for " + this + " but the instance cannot be found.  Please check this list of hoverExtenders: " + this.hoverExtenders + " for the correct names.");
						}
					}
					this.hoverExtenders = hoverExtenders;
				} else {
					this.hoverExtenders ||= [];
				}
				var hit = this.hit || this;
				hit.addEventListener(FocusEvent.FOCUS_OUT, outHandler);
				hit.addEventListener(MouseEvent.MOUSE_OUT, outHandler);
				for each (var extender:DisplayObject in this.hoverExtenders) {
					extender.addEventListener(MouseEvent.MOUSE_OUT, outHandler);
				}
				var o:DisplayObject = this;
				function outHandler(e:Event) {
					if (e is MouseEvent) {
						// allow the mouse to move freely between this object and any of the hover 
						// extenders without triggering the out handler
						for each (var hoverable:DisplayObject in [hit, o.hoverExtenders].flatten()) {
							if (hoverable["isThisOrChildTopObjectUnderPoint"](MouseEvent(e).stageX, MouseEvent(e).stageY, true)) {
								return;
							}
						}
						if (Global["hover"] == o) {
							Global["hover"] = null;
						}
					}
					if (Global.stage.focus != o && Global["hover"] != o && !o["preventMouseOut"]) {
						o.doEvent("blur");
					}
				}
			}
		}
	}
}